      
## coding=utf-8
from __future__ import print_function, absolute_import, unicode_literals
import numpy as np
import pandas as pd
from gm.api import *
import datetime


'''
示例策略仅供参考，不建议直接实盘使用。

本策略以0.8为初始权重跟踪指数标的沪深300中权重大于0.35%的成份股.
个股所占的百分比为(0.8*成份股权重)*100%.然后根据个股是否:
1.连续上涨5天 2.连续下跌5天
来判定个股是否为强势股/弱势股,并对其把权重由0.8调至1.0或0.6
'''


def init(context):
    # 强势股/弱势股判断周期，5天
    context.days_num = 5
    # 资产配置的初始权重,配比为0.6-0.8-1.0
    context.high_ratio = 1.0
    context.middle_ratio = 0.8
    context.low_ratio = 0.6
    context.index_symbol = 'SHSE.000300'
    # 权重阈值
    context.Threshold_weight = 0.0035
    # 每天09:30 定时执行algo任务
    schedule(schedule_func=algo, date_rule='1d', time_rule='09:30:00')


def algo(context):    
    # 回测开始时间的上一交易日
    last_date = get_previous_trading_date(exchange='SZSE', date=context.now)
    # 获取沪深300当时的成份股和相关数据
    stock300 = get_history_constituents(index=context.index_symbol, start_date=last_date, end_date=last_date)[0]['constituents']
    stock300 = (pd.DataFrame(stock300,index=['weight']).T)/100
    stock300 = stock300[stock300['weight']>context.Threshold_weight]
    to_buy = list(stock300.index)
    print('{},选择的成分股权重总和为:{:.4f}'.format(context.now,np.sum(stock300)[0]))
    
    # 获取context.days_num+1个交易日前的日期
    pre_n_day = get_previous_N_trading_date(last_date,counts=context.days_num+1)
    # 获取数据并转换成date*symbol的形式
    history_data = history(symbol=','.join(to_buy), frequency='1d', start_time=pre_n_day,  end_time=last_date, fields='symbol, close, eob', adjust=ADJUST_PREV, df= True).set_index(['eob','symbol'])
    history_data = history_data.unstack().fillna(0)
    history_data.columns = history_data.columns.droplevel(level=0)
    # 筛选强势股
    continur_up_info = (history_data>history_data.shift(1)).iloc[-context.days_num:,:].sum()
    up_symbol = continur_up_info[continur_up_info==context.days_num]
    up_symbol = list(up_symbol.index) if len(up_symbol)>0 else []
    # 筛选弱势股
    continur_down_info = (history_data<history_data.shift(1)).iloc[-context.days_num:,:].sum()
    down_symbol = continur_down_info[continur_down_info==context.days_num]
    down_symbol = list(down_symbol.index) if len(down_symbol)>0 else []
    # 普通股票（非强势股，非弱势股）
    common_symbol = list(set(history_data.columns)-set(up_symbol)-set(down_symbol))

    ## 股票交易
    # 获取持仓
    positions = context.account().positions()
    holding = [position['symbol'] for position in positions]

    # 卖出不在to_buy中的持仓
    for position in positions:
        symbol = position['symbol']
        if symbol not in to_buy:
            order_target_percent(symbol=symbol, percent=0, order_type=OrderType_Market, position_side=PositionSide_Long)

    # 买入股票（强势股）
    for symbol in set(up_symbol)-set(holding):
        buy_percent = stock300['weight'][symbol] * context.high_ratio
        order_target_percent(symbol=symbol, percent=buy_percent, order_type=OrderType_Market, position_side=PositionSide_Long)

    # 买入股票（弱势股）
    for symbol in set(down_symbol)-set(holding):
        buy_percent = stock300['weight'][symbol] * context.low_ratio
        order_target_percent(symbol=symbol, percent=buy_percent, order_type=OrderType_Market, position_side=PositionSide_Long)
        
    # 买入股票（普通股）
    for symbol in set(common_symbol)-set(holding):
        buy_percent = stock300['weight'][symbol] * context.middle_ratio
        order_target_percent(symbol=symbol, percent=buy_percent, order_type=OrderType_Market, position_side=PositionSide_Long)


def on_order_status(context, order):
    # 标的代码
    symbol = order['symbol']
    # 委托价格
    price = order['price']
    # 委托数量
    volume = order['volume']
    # 目标仓位
    target_percent = order['target_percent']
    # 查看下单后的委托状态，等于3代表委托全部成交
    status = order['status']
    # 买卖方向，1为买入，2为卖出
    side = order['side']
    # 开平仓类型，1为开仓，2为平仓
    effect = order['position_effect']
    # 委托类型，1为限价委托，2为市价委托
    order_type = order['order_type']
    if status == 3:
        if effect == 1:
            if side == 1:
                side_effect = '开多仓'
            elif side == 2:
                side_effect = '开空仓'
        else:
            if side == 1:
                side_effect = '平空仓'
            elif side == 2:
                side_effect = '平多仓'
        order_type_word = '限价' if order_type==1 else '市价'
        print('{}:标的：{}，操作：以{}{}，委托价格：{}，目标仓位：{:.2%}'.format(context.now,symbol,order_type_word,side_effect,price,target_percent))


def get_previous_N_trading_date(date,counts=1):
    """
    获取end_date前N个交易日
    :param date：目标日期
    :param counts：历史回溯天数，默认为1，即前一天
    """
    if isinstance(date,str) and len(date)==10:
        date = datetime.datetime.strptime(date,'%Y-%m-%d')
    elif isinstance(date,str) and len(date)==19:
        date = datetime.datetime.strptime(date,'%Y-%m-%d %H:%M:%S')
    previous_N_trading_date = get_trading_dates(exchange='SHSE', start_date=date-datetime.timedelta(days=counts+30), end_date=date)[-counts]
    return previous_N_trading_date


def on_backtest_finished(context, indicator):
    print('*'*50)
    print('回测已完成，请通过右上角“回测历史”功能查询详情。')


if __name__ == '__main__':
    '''
    strategy_id策略ID,由系统生成
    filename文件名,请与本文件名保持一致
    mode实时模式:MODE_LIVE回测模式:MODE_BACKTEST
    token绑定计算机的ID,可在系统设置-密钥管理中生成
    backtest_start_time回测开始时间
    backtest_end_time回测结束时间
    backtest_adjust股票复权方式不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST
    backtest_initial_cash回测初始资金
    backtest_commission_ratio回测佣金比例
    backtest_slippage_ratio回测滑点比例
    '''
    run(strategy_id='99d0d168-4a90-11ed-b994-709cd153e504',
        filename='main.py',
        mode=MODE_BACKTEST,
        token='c53e8063c60aa6923bd558bebc59b30f51d22c23',
        backtest_start_time='2019-01-01 08:00:00',
        backtest_end_time='2019-04-30 16:00:00',
        backtest_adjust=ADJUST_PREV,
        backtest_initial_cash=10000000,
        backtest_commission_ratio=0.0001,
        backtest_slippage_ratio=0.0001)